/***********************************************
 This file is part of redia-lib-utils-ios
.
 
 Copyright (c) 2012, 2013 Redia A/S
 
 Redia-library-app-ios is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 Redia-library-app-ios is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with redia-library-app-ios.  If not, see <http://www.gnu.org/licenses/>.
 
 *********************************************** */


#import "UrlCacheManager.h"
#import "defines.h"
#import "UrlCacheImageEntry.h"


@implementation UrlCacheManager

static UrlCacheManager *sharedSingleton;

@synthesize databasePath, cacheFilesPath;
@synthesize isOffLineMode;
@synthesize defaultCacheFileExpireTimeSeconds;

+ (void)initialize
{
    static BOOL initialized = NO;
    if(!initialized)
    {
        initialized = YES;
        sharedSingleton = [[UrlCacheManager alloc] init];
		sharedSingleton->defaultCacheFileExpireTimeSeconds = 5*24*60*60; // 5 days
    }
}

+ (UrlCacheManager*)instance
{
	return sharedSingleton;
}

- (id)init
{
    self = [super init];
    if (self) {
		isOffLineMode = NO;
		
		[self setupDB];
    }
    return self;
}

- (void)setupPath
{
	NSString *docsDir;
	NSArray *dirPaths;
	
	// Get the directory for the sql db
#ifndef INFOOBJECT_USE_DOCUMENTS_DIR
    // legacy dbs use cache dir
    dirPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
#else
    dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
#endif
	
	
	docsDir = [dirPaths objectAtIndex:0];
	// Build the path to the database file
	databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: @"cache-v03.db"]];
    DLog(@"cache db path is %@",databasePath);
}

- (void)setupDB
{
	[self setupPath];
    
	BOOL doCreateNewTable = NO;
    
	NSFileManager *filemgr = [NSFileManager defaultManager];
    if ([filemgr fileExistsAtPath: databasePath ] == NO) {
        doCreateNewTable=YES;
    }
    
    const char *dbpath = [databasePath UTF8String];
    
    if (sqlite3_open(dbpath, &cacheDB) == SQLITE_OK)
    {
        if (doCreateNewTable)
        {
			char *errMsg;
			const char *sql_stmt = "CREATE TABLE IF NOT EXISTS CACHE (ID INTEGER PRIMARY KEY AUTOINCREMENT, URL TEXT, FILENAME TEXT, FILETYPE TEXT, LASTMODIFIED TEXT, WIDTH TEXT, HEIGHT TEXT, TYPE TEXT, EXPIRES TEXT, FILESIZE TEXT); CREATE INDEX IF NOT EXISTS urlidx ON cache(url); CREATE INDEX IF NOT EXISTS fileidx ON cache(filename)";
			
			if (sqlite3_exec(cacheDB, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
			{
				NSLog(@"Failed to create table or index: %s",errMsg);
			}
		}
	} else {
        NSLog(@"Failed to open/create database");
    }
	
	NSArray* cacheDirs = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
	cacheFilesPath = [cacheDirs objectAtIndex:0];
	cacheFilesPath = [[NSString alloc] initWithString: [cacheFilesPath stringByAppendingPathComponent:@"cachefiles"]];
	if ([filemgr fileExistsAtPath:cacheFilesPath]==NO) {
		[filemgr createDirectoryAtPath:cacheFilesPath withIntermediateDirectories:YES attributes:nil error:nil];
	}
	DLog(@"cache files path is %@",cacheFilesPath);
	
	
    
}


- (void)closeCacheDb
{
    NSLog(@"Closing cache db");
    sqlite3_close(cacheDB);
    cacheDB=nil;
}

#pragma mark - Image handling

-(void)saveImage:(NSData *)imageData fromUrl:(NSURL *)url imageWidth:(int)width imageHeight:(int)height
{
    [self saveImage:imageData
            fromUrl:url
         imageWidth:width
        imageHeight:height
         expireTime:[[NSDate date] timeIntervalSince1970]+defaultCacheFileExpireTimeSeconds];
}

-(void)saveImage:(NSData*)imageData fromUrl:(NSURL*)url imageWidth:(int)imageWidth imageHeight:(int)imageHeight expireTime:(NSTimeInterval)expireUnixTime
{
	sqlite3_stmt    *statement;
	sqlite3_stmt    *statement2;
	
	
    NSDate* now = [NSDate date];
    
    NSString *insertSQL = [NSString stringWithFormat: @"INSERT INTO CACHE (url, filetype, LASTMODIFIED, width, height, type, expires) VALUES (\"%@\" , \"\", \"%lld\", \"%d\", \"%d\", \"IMAGE\", \"%lld\")",
                           url,
                           ((long long int) [now timeIntervalSince1970]),
                           imageWidth,
                           imageHeight,
                           ((long long int) [now timeIntervalSince1970]) + defaultCacheFileExpireTimeSeconds
                           ];
    
    const char *insert_stmt = [insertSQL UTF8String];
    
    sqlite3_prepare_v2(cacheDB, insert_stmt, -1, &statement, NULL);
    if (sqlite3_step(statement) == SQLITE_DONE)
    {
        sqlite3_int64 newrow = sqlite3_last_insert_rowid(cacheDB);
        NSString* filename = [NSString stringWithFormat:@"%lld.dat",newrow];
        
        NSString *updateSQL = [NSString stringWithFormat:@"UPDATE CACHE SET filename = \"%@\", filesize = \"%lld\" WHERE ID = %lld",
                               filename,
                               ((long long int) [imageData length]),
                               newrow];
        
        const char *update_stmt = [updateSQL UTF8String];
        
        sqlite3_prepare_v2(cacheDB, update_stmt, -1, &statement2, NULL);
        if (sqlite3_step(statement2) == SQLITE_DONE)
        {
            //DLog(@"INSERTED OK!!!");
            filename = [cacheFilesPath stringByAppendingPathComponent:filename];
            //DLog(@"filename %@",filename);
            NSError* error = nil;
            if ([imageData writeToFile:filename options:0 error:&error]) {
                //DLog(@"file written");
            } else {
                DLog(@"file ERROR! %@",[error localizedDescription]);
            }
        } else {
            ALog(@"Failed to add cache file");
        }
        sqlite3_finalize(statement2);
    } else {
        const char * msg = sqlite3_errmsg(cacheDB);
        ALog(@"Failed to add cache, %s",msg);
        
    }
    sqlite3_finalize(statement);
	
}

-(NSString *)getLocalCacheFileNameForVideoUrl:(NSURL *)url size:(CGSize *)out_size initiateDownload:(BOOL)do_download downloadProgressDelegate:(id<ProgressDisplayDelegate>)progressDelegate
{
    return nil;
}

- (BOOL)checkIsExpiredFromExpireTimeString:(NSString*)expireTimeString
{
    NSTimeInterval i = [expireTimeString doubleValue];
    NSDate* timestamp = [NSDate dateWithTimeIntervalSince1970:i];
    return ([timestamp timeIntervalSinceNow]<0.0 && !isOffLineMode);
}

- (NSString*) getLocalCacheFilePath
{
    return cacheFilesPath;
}

- (NSURL*) getLocalCacheFileBaseUrl
{
    return [NSURL fileURLWithPath:cacheFilesPath];
}

- (NSString*)getLocalCacheFilePathForUrl:(NSURL*)url withFileType:(UrlCacheManagerFileType)fileType{
    NSString* fileName = [self getLocalCacheFileNameForUrl:url withFileType:fileType];
    if (fileName == nil) {
        return nil;
    }
    return [NSString stringWithFormat:@"%@/%@", self.cacheFilesPath, fileName];
}

-(NSString *)getLocalCacheFileNameForUrl:(NSURL *)url withFileType:(UrlCacheManagerFileType)fileType
{
    return [self getLocalCacheFileNameForUrl:url withFileType:fileType size:nil initiateDownload:NO];
}

-(NSString *)getLocalCacheFileNameForImageUrl:(NSURL *)url size:(CGSize*)out_size
{
    return [self getLocalCacheFileNameForUrl:url withFileType:UrlCacheManagerFileTypeImage size:out_size initiateDownload:YES];
}

-(NSString *)getLocalCacheFileNameForImageUrl:(NSURL *)url size:(CGSize*)out_size initiateDownload:(BOOL)do_download
{
    return [self getLocalCacheFileNameForUrl:url withFileType:UrlCacheManagerFileTypeImage size:out_size initiateDownload:do_download];
}


-(NSString *)getLocalCacheFileNameForUrl:(NSURL *)url withFileType:(UrlCacheManagerFileType)fileType size:(CGSize*)out_size initiateDownload:(BOOL)do_download
{
	sqlite3_stmt    *statement;
	
	sqlite_int64 row_id = -1;
	bool remove_entry = false;
    
    NSString* result_filename = nil;
    
    NSString* filetypeString = @"";
    
    if (fileType==UrlCacheManagerFileTypeImage) {
        filetypeString = @"AND type=\"IMAGE\"";
        
    } else if (fileType==UrlCacheManagerFileTypeVideo) {
        filetypeString = @"AND type=\"VIDEO\"";
    }
	
    NSString *querySQL = [NSString stringWithFormat: @"SELECT filename, LASTMODIFIED, id, width, height, expires FROM cache WHERE url=\"%@\" %@",
                          url,
                          filetypeString
                          ];
    
    const char *query_stmt = [querySQL UTF8String];
    
    if (sqlite3_prepare_v2(cacheDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
    {
        if (sqlite3_step(statement) == SQLITE_ROW)
        {
            const char* file_c_str = (const char *) sqlite3_column_text(statement, 0);
            if (file_c_str==nil) {
                remove_entry=true;
            } else {
                NSString *filename = [[NSString alloc] initWithUTF8String:file_c_str];
                //NSString *filename_fullpath = [cacheFilesPath stringByAppendingPathComponent:filename];
                
                //NSString *modifiedtime = [[NSString alloc] initWithUTF8String:((const char *) sqlite3_column_text(statement, 1) ? : "")];

                NSString *expiretime = [[NSString alloc] initWithUTF8String:((const char *) sqlite3_column_text(statement, 5) ? : "")];

                row_id = sqlite3_column_int64(statement, 2);
                
                if ([self checkIsExpiredFromExpireTimeString:expiretime]) {
                    DLog(@"cache expired");
                    remove_entry=true;
                } else {
                    result_filename = filename;
                    
                    if (out_size!=nil) {
                        const char* imgwidth_c_str = (const char *) sqlite3_column_text(statement, 3);
                        const char* imgheight_c_str = (const char *) sqlite3_column_text(statement, 4);
                        
                        if (imgwidth_c_str!=nil && imgheight_c_str!=nil) {
                            out_size->width = atoi(imgwidth_c_str);
                            out_size->height = atoi(imgheight_c_str);
                            
                        }
                    }
                }
            }
        }
        sqlite3_finalize(statement);
    }
    if (remove_entry && row_id!=-1) {
        [self removeEntry:row_id];
    }
    
    if (do_download) {
        [self loadImageUrl:url delegate:nil doDownloadOnly:YES];
    }
    
    //commence download if not found in cache
    return result_filename;
}




#pragma mark - Video handling

-(NSString*)saveVideoFromTempFilePath:(NSString *)tempFilePath fromUrl:(NSURL *)url expireTime:(NSTimeInterval)expireUnixTime
{
	sqlite3_stmt    *statement;
	sqlite3_stmt    *statement2;
	NSString* new_filename = nil;

    NSFileManager* file_mgr = [NSFileManager defaultManager];
    if (![file_mgr fileExistsAtPath:tempFilePath]) {
        NSLog(@"Error: no file at tempFilePath %@",tempFilePath);
        return nil;
    }

    NSError* getAttributesError;
    NSDictionary* file_attributes = [file_mgr attributesOfItemAtPath:tempFilePath error:&getAttributesError];
    if (getAttributesError!=nil || file_attributes==nil) {
        NSLog(@"Error getting file size= %@",getAttributesError);
        return nil;
    }
    unsigned long long file_size = [file_attributes fileSize];

    NSDate* now = [NSDate date];
    
    NSString *insertSQL = [NSString stringWithFormat: @"INSERT INTO CACHE (url, filetype, LASTMODIFIED, width, height, type, expires) VALUES (\"%@\" , \"\", \"%lld\", \"0\", \"0\", \"VIDEO\", \"%lld\")",
                           url,
                           ((long long int) [now timeIntervalSince1970]),
                           ((long long int) [now timeIntervalSince1970]) + defaultCacheFileExpireTimeSeconds
                           ];
    
    const char *insert_stmt = [insertSQL UTF8String];
    
    
    sqlite3_prepare_v2(cacheDB, insert_stmt, -1, &statement, NULL);
    if (sqlite3_step(statement) == SQLITE_DONE)
    {
        sqlite3_int64 newrow = sqlite3_last_insert_rowid(cacheDB);
        new_filename = [NSString stringWithFormat:@"%lld.mp4",newrow];
        
        
        
        NSString *updateSQL = [NSString stringWithFormat:@"UPDATE CACHE SET filename = \"%@\", filesize = \"%llu\" WHERE ID = %lld",
                               new_filename,
                               file_size,
                               newrow];
        
        const char *update_stmt = [updateSQL UTF8String];
        
        sqlite3_prepare_v2(cacheDB, update_stmt, -1, &statement2, NULL);
        if (sqlite3_step(statement2) == SQLITE_DONE)
        {
            //DLog(@"INSERTED OK!!!");
            new_filename = [cacheFilesPath stringByAppendingPathComponent:new_filename];
            //DLog(@"filename %@",filename);
            NSError* moveFileError;
            if (![file_mgr moveItemAtPath:tempFilePath toPath:new_filename error:&moveFileError]) {
                DLog(@"file ERROR! %@",[moveFileError localizedDescription]);
                new_filename = nil;
            }
        } else {
            ALog(@"Failed to add cache file");
            new_filename = nil;
        }
        sqlite3_finalize(statement2);
    } else {
        const char * msg = sqlite3_errmsg(cacheDB);
        ALog(@"Failed to add cache, %s",msg);
        
    }
    sqlite3_finalize(statement);
    
    return new_filename;
}



- (UrlCacheImageEntry*) loadImageUrl:(NSURL*)url delegate:(id<UrlCacheEntryDelegate>)delegate
{
    return [self loadImageUrl:url delegate:delegate doDownloadOnly:NO];
}

- (UrlCacheImageEntry*) loadImageUrl:(NSURL*)url delegate:(id<UrlCacheEntryDelegate>)delegate doDownloadOnly:(BOOL)download_only
{
	sqlite3_stmt    *statement;
	
	sqlite_int64 row_id = -1;
	bool remove_entry = false;
	bool download_url = false;
    
	UrlCacheImageEntry* newentry = nil;
    
    NSString *querySQL = [NSString stringWithFormat: @"SELECT filename, LASTMODIFIED, id, expires  FROM cache WHERE url=\"%@\" AND type=\"IMAGE\"", url];
    
    const char *query_stmt = [querySQL UTF8String];
    int sqlerror=0;
    if ((sqlerror=sqlite3_prepare_v2(cacheDB, query_stmt, -1, &statement, NULL)) == SQLITE_OK)
    {
        if (sqlite3_step(statement) == SQLITE_ROW)
        {
            const char* file_c_str = (const char *) sqlite3_column_text(statement, 0);
            if (file_c_str==nil) {
                remove_entry=true;
                download_url=true;
            } else {
                NSString *filename = [[NSString alloc] initWithUTF8String:file_c_str];
                NSString *filename_fullpath = [cacheFilesPath stringByAppendingPathComponent:filename];
                
                //NSString *modifiedtime = [[NSString alloc] initWithUTF8String:((const char *) sqlite3_column_text(statement, 1) ? : "")];

                NSString *expiretime = [[NSString alloc] initWithUTF8String:((const char *) sqlite3_column_text(statement, 3) ? : "")];

                row_id = sqlite3_column_int64(statement, 2);
                
                if ([self checkIsExpiredFromExpireTimeString:expiretime]) {
                    DLog(@"cache expired");
                    remove_entry=true;
                    download_url=true;
                } else {
                    NSFileManager *filemgr = [NSFileManager defaultManager];
                    if ([filemgr fileExistsAtPath: filename_fullpath ] == YES) {
                        DLog(@"cache hit");
                        if (!download_only) {
                            UIImage* newimage = [[UIImage alloc] initWithContentsOfFile:filename_fullpath];
                            if (newimage!=nil) {
                                newentry = [UrlCacheImageEntry new];
                                newentry.fetchFromUrl = url;
                                newentry.resultImage = newimage;
                                [delegate  cacheEntry:newentry didLoadObjectFromUrl:url ofType:UrlCacheEntryTypeImage];
                            } else {
                                DLog(@"error loading image data from cache");
                                remove_entry=true;
                                download_url=true;
                            }
                            
                        } else {
                            newentry = [UrlCacheImageEntry new];
                            newentry.fetchFromUrl = url;

                            [delegate cacheEntry:newentry didLoadObjectFromUrl:url ofType:UrlCacheEntryTypeImage]; //callback without image since we don't need the image right now
                        }
                        
                    } else {
                        DLog(@"cache db hit but file missing");
                        remove_entry=true;
                        download_url=true;
                    }
                }
            }
            
        } else {
            DLog(@"cache miss");
            download_url=true;
        }
        sqlite3_finalize(statement);
    } else {
        ALog(@"SQLite error %d",sqlerror);
    }
    
    if (remove_entry && row_id!=-1) {
        [self removeEntry:row_id];
    }
    
	
	if (download_url) {
		newentry = [UrlCacheImageEntry new];
		[newentry loadImageFromURL:url delegate:delegate];
	}
	return newentry;
	
}

-(UrlCacheVideoEntry *)loadVideoUrl:(NSURL *)url delegate:(id<UrlCacheEntryDelegate>)delegate
{
	sqlite3_stmt    *statement;
	
	sqlite_int64 row_id = -1;
	bool remove_entry = false;
	bool download_url = false;
    
	UrlCacheVideoEntry* newentry = nil;
    
    NSString *querySQL = [NSString stringWithFormat: @"SELECT filename, LASTMODIFIED, id, expires  FROM cache WHERE url=\"%@\" AND type=\"VIDEO\"", url];
    
    const char *query_stmt = [querySQL UTF8String];
    int sqlerror=0;
    if ((sqlerror=sqlite3_prepare_v2(cacheDB, query_stmt, -1, &statement, NULL)) == SQLITE_OK)
    {
        if (sqlite3_step(statement) == SQLITE_ROW)
        {
            const char* file_c_str = (const char *) sqlite3_column_text(statement, 0);
            if (file_c_str==nil) {
                remove_entry=true;
                download_url=true;
            } else {
                NSString *filename = [[NSString alloc] initWithUTF8String:file_c_str];
                NSString *filename_fullpath = [cacheFilesPath stringByAppendingPathComponent:filename];
                
                //NSString *modifiedtime = [[NSString alloc] initWithUTF8String:((const char *) sqlite3_column_text(statement, 1) ? : "")];
                
                NSString *expiretime = [[NSString alloc] initWithUTF8String:((const char *) sqlite3_column_text(statement, 3) ? : "")];
                
                row_id = sqlite3_column_int64(statement, 2);
                
                if ([self checkIsExpiredFromExpireTimeString:expiretime]) {
                    DLog(@"cache expired");
                    remove_entry=true;
                    download_url=true;
                } else {
                    NSFileManager *filemgr = [NSFileManager defaultManager];
                    if ([filemgr fileExistsAtPath: filename_fullpath ] == YES) {
                        DLog(@"cache hit");
                        
                        newentry = [UrlCacheVideoEntry new];
                        newentry.fetchFromUrl = url;
                        newentry.videoLocalFilePath = filename_fullpath;
                        [delegate  cacheEntry:newentry didLoadObjectFromUrl:url ofType:UrlCacheEntryTypeVideo];
                        
                    } else {
                        DLog(@"cache db hit but file missing");
                        remove_entry=true;
                        download_url=true;
                    }
                }
            }
            
        } else {
            DLog(@"cache miss");
            download_url=true;
        }
        sqlite3_finalize(statement);
    } else {
        ALog(@"SQLite error %d",sqlerror);
    }
    
    if (remove_entry && row_id!=-1) {
        [self removeEntry:row_id];
    }
    
	
	if (download_url) {
		newentry = [UrlCacheVideoEntry new];
		[newentry loadVideoFromUrl:url delegate:delegate];
	}
	return newentry;
	
}



#pragma mark - Database maintenance


- (void) removeCacheFile:(NSString*) filename
{
	NSString* filename_fullpath = [cacheFilesPath stringByAppendingPathComponent:filename];
	NSFileManager *filemgr = [NSFileManager defaultManager];
	
	if ([filemgr fileExistsAtPath: filename_fullpath ] == YES) {
		if (![filemgr removeItemAtPath:filename_fullpath error:nil]) {
			DLog(@"problem removing file %@", filename_fullpath);
		}
	} else {
		DLog(@"file wasn't there (already removed): %@",filename);
	}
    
}


- (void) removeEntry:(sqlite_int64)row_id
{
	sqlite3_stmt    *statement;
	
    NSString *querySQL = [NSString stringWithFormat: @"SELECT filename, url FROM cache WHERE id=%lld", row_id];
    
    const char *query_stmt = [querySQL UTF8String];
    
    if (sqlite3_prepare_v2(cacheDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
    {
        if (sqlite3_step(statement) == SQLITE_ROW)
        {
            //NSString *url = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 1)];
            const char* url_cstr = (const char *) sqlite3_column_text(statement, 1);
            
            sqlite3_stmt    *statement2;
            NSString *updateSQL;
            if (url_cstr!=nil && url_cstr[0]!='\0') {
                //make sure to delete all rows with this url
                updateSQL = [NSString stringWithFormat:@"DELETE FROM cache WHERE url=\"%s\"",url_cstr];
            } else {
                updateSQL = [NSString stringWithFormat:@"DELETE FROM cache WHERE id=%lld",row_id];
            }
            
            const char *update_stmt = [updateSQL UTF8String];
            
            sqlite3_prepare_v2(cacheDB, update_stmt, -1, &statement2, NULL);
            if (sqlite3_step(statement2) == SQLITE_DONE)
            {
                DLog("successfully removed url from cache db");
            }
            
            const char* file_cstr = (const char *) sqlite3_column_text(statement, 0);
            if (file_cstr!=nil) {
                NSString *filename = [[NSString alloc] initWithUTF8String:file_cstr];
                [self removeCacheFile:filename];
            }
            
            
            //[url release];
        } else {
            DLog(@"can't remove entry from db");
        }
        sqlite3_finalize(statement);
    }
	
}

- (BOOL)checkFilenameInDB:(NSString*)filename
{
	sqlite3_stmt    *statement;
	
	BOOL result = FALSE;
	
    NSString *querySQL = [NSString stringWithFormat: @"SELECT COUNT(*) FROM cache WHERE filename=\"%@\"", filename];
    
    const char *query_stmt = [querySQL UTF8String];
    
    if (sqlite3_prepare_v2(cacheDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
    {
        if (sqlite3_step(statement) == SQLITE_ROW)
        {
            result = (sqlite3_column_int(statement, 0) > 0);
            
        }
        sqlite3_finalize(statement);
    }
	
	return result;
}

- (void)checkConsistency
{
    [self checkConsistencyWithProgressDelegate:nil];
}

- (void)checkConsistencyWithProgressDelegate:(id<ProgressDisplayDelegate>)_delegate
{
	DLog(@"checking DB consistency...");
	
	NSFileManager *filemgr = [NSFileManager defaultManager];
    
    int num_files=0;
    
    if (_delegate!=nil && [_delegate respondsToSelector:@selector(progressText:)]) {
        [_delegate progressText:@"Preparing cache..."];
    }
    
	NSError* error;
	NSArray* files = [filemgr contentsOfDirectoryAtPath:cacheFilesPath error:&error];
	NSMutableArray* files_to_remove = [NSMutableArray new];
    
    /* already done in create table
    const char *query_stmt4 = "CREATE INDEX IF NOT EXISTS fileidx ON cache(filename)";
    sqlite3_stmt    *statement4;
    
    int result=0;
    if ((result=sqlite3_prepare_v2(cacheDB, query_stmt4, -1, &statement4, NULL)) == SQLITE_OK)
    {
        result = sqlite3_step(statement4);
        DLog(@"result %d",result);
        sqlite3_finalize(statement4);
    }
    
    const char *query_stmt5 = "CREATE INDEX IF NOT EXISTS urlidx ON cache(url);";
    sqlite3_stmt    *statement5;
    
    if ((result=sqlite3_prepare_v2(cacheDB, query_stmt5, -1, &statement5, NULL)) == SQLITE_OK)
    {
        result = sqlite3_step(statement5);
        DLog(@"result %d",result);
        sqlite3_finalize(statement5);
    }
    */
    
    //check that all files are ref'd from db
    for (NSString* fname in files) {
        BOOL is_in_db = [self checkFilenameInDB:fname];
        //DLog(@"filename %@ is in db: %d",fname, is_in_db);
        
        if (!is_in_db) {
            [files_to_remove addObject:fname];
        }
        num_files++;
        if (_delegate!=nil && num_files%100==0) {
            if ([_delegate respondsToSelector:@selector(progress:)]) {
                [_delegate progress:((float)num_files/((float)[files count]))];
                [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];
            }
        }
    }
    
	
	//remove orphan files
	for (NSString* fname2 in files_to_remove) {
		DLog(@"removing file %@ from cache",fname2);
		[self removeCacheFile:fname2];
	}
	
	
	//check that DB only references files that exist and which aren't expired
	sqlite3_stmt    *statement;
	NSMutableArray* entries_to_remove = [NSMutableArray new];
    int sqlrow=0;
	
    NSString *querySQL = [NSString stringWithFormat: @"SELECT filename, LASTMODIFIED, id FROM cache"];
    
    const char *query_stmt = [querySQL UTF8String];
    
    if (sqlite3_prepare_v2(cacheDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
    {
        while (sqlite3_step(statement) == SQLITE_ROW)
        {
            bool do_remove = false;
            NSString *modifiedtime = [[NSString alloc] initWithUTF8String:((const char *) sqlite3_column_text(statement, 1) ? : "")];
            NSTimeInterval i = [modifiedtime doubleValue] + defaultCacheFileExpireTimeSeconds;
            NSDate* timestamp = [NSDate dateWithTimeIntervalSince1970:i];
            
            if ([timestamp timeIntervalSinceNow]<0.0) {
                DLog(@"cache expired");
                do_remove = true;
            } else {
                const unsigned char *filename_cstr = sqlite3_column_text(statement, 0);
                if (filename_cstr!=nil) {
                    NSString* filename = [[NSString alloc] initWithUTF8String:((const char *) filename_cstr ? : "")];
                    NSString *filename_fullpath = [cacheFilesPath stringByAppendingPathComponent:filename];
                    
                    //hent filnavn og check om det er i pathen
                    if ([filemgr fileExistsAtPath: filename_fullpath ] == NO)  {
                        DLog(@"db entry's file missing");
                        do_remove = true;
                    }
                } else {
                    do_remove=true;
                }
            }
            
            
            if (do_remove) {
                [entries_to_remove addObject:[NSNumber numberWithLongLong:sqlite3_column_int64(statement, 2)]];
                
            }
            sqlrow++;
            if (_delegate!=nil && sqlrow%100==0) {
                if ([_delegate respondsToSelector:@selector(progress:)]) {
                    [_delegate progress:((float)sqlrow/((float)num_files))];
                    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];
                }
            }
            
        }
        
        sqlite3_finalize(statement);
    } else {
        DLog(@"sqlite reported error");
    }
    
    //remove orphan entries
    for (NSNumber* num in entries_to_remove) {
        DLog(@"removing entry %lld from cache",[num longLongValue]);
        [self removeEntry:[num longLongValue]];
    }
   
	DLog(@"checking DB consistency done.");
}

- (void)clearEntireCacheDatabase
{
	NSFileManager *filemgr = [NSFileManager defaultManager];
	NSError* error;
	[filemgr removeItemAtPath:databasePath error:&error];
	
	[self setupDB];
	[self checkConsistency]; //effectively remove all files
}


@end
